/*
 * IFilter.cs
 * 
 * Microsoft Office SharePoint Server Managed Protocol Handler
 * 
 * Author: John Kozell (johnkoz@microsoft.com)
 *         Microsoft Coroporation
 * 
 * This code is an adaptation from, and interpretation of, the original SPS 2001 SDK
 * and has been updated with available information at the time of writing.
 * Not all the delcarions and alogrithms have been verified and tested
 * and there is no guarantee that they are accurate or valid, now or in the future.
 * 
 * The .NET Managed COM Interop approach contained herin was NOT created in cooperation
 * with the Microsoft SharePoint product group and has not been officially endorced
 * by Microsoft and therefore is not supported. The user of this code assumes any
 * and all risk with its use and should proceed with discression.
 * 
 * THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 */

using System;
using System.Text;

using System.Runtime.InteropServices;

#region "Windows Data Types"
// http://msdn.microsoft.com/en-us/library/aa383751(VS.85).aspx
using BOOL = System.Int32;     // typedef int BOOL;
using WORD = System.UInt16;    // typedef unsigned short WORD;
using DWORD = System.UInt32;   // typedef unsigned long DWORD;
using SHORT = System.Int16;    // typedef short SHORT;
using USHORT = System.UInt16;  // typedef unsigned short USHORT;
using INT = System.Int32;      // typedef int INT;
using UINT = System.UInt32;    // typedef unsigned int UINT;
using LONG = System.Int32;     // typedef long LONG;
using ULONG = System.UInt32;   // typedef unsigned long ULONG;
using FLOAT = System.Single;   // typedef float FLOAT;
using LPCWSTR = System.String; // typedef CONST WCHAR *LPCWSTR; 
using LPWSTR = System.String;  // typedef WCHAR *LPWSTR;
using LCID = System.UInt32;    // typedef DWORD LCID;

using CLSID = System.Guid;
using VARTYPE = System.UInt16;
using PROPID = System.UInt32;
using DOUBLE = System.Double;
#endregion


[Flags]
public enum IFILTER_INIT : uint
{
	NONE                    = 0,
	CANON_PARAGRAPHS        = 1,
	HARD_LINE_BREAKS        = 2,
	CANON_HYPHENS           = 4,
	CANON_SPACES            = 8,
	APPLY_INDEX_ATTRIBUTES  = 16,
	APPLY_CRAWL_ATTRIBUTES  = 256,
	APPLY_OTHER_ATTRIBUTES  = 32,
	INDEXING_ONLY           = 64,
	SEARCH_LINKS            = 128,        
	FILTER_OWNED_VALUE_OK   = 512,
    FILTER_AGGRESSIVE_BREAK	= 1024,
	DISABLE_EMBEDDED        = 2048,
	EMIT_FORMATTING         = 4096

}


[Flags]
public enum IFILTER_FLAGS
{
	OLE_PROPERTIES = 1
}


public enum CHUNK_BREAKTYPE : uint
{
	CHUNK_NO_BREAK = 0,
	CHUNK_EOW      = 1,
	CHUNK_EOS      = 2,
	CHUNK_EOP      = 3,
	CHUNK_EOC      = 4
}


[Flags]
public enum CHUNKSTATE : uint
{
	CHUNK_TEXT               = 0x1,
	CHUNK_VALUE              = 0x2,
	CHUNK_FILTER_OWNED_VALUE = 0x4
}


public enum PRSPEC : uint
{
	LPWSTR = 0,
	PROPID = 1
}

[StructLayout(LayoutKind.Explicit)]
public struct PROPSPEC_UNION
{
	[FieldOffset(0)] 
    public PROPID propid;
	[FieldOffset(0)] 
    public IntPtr lpwstr;
}


public struct PROPSPEC
{
	public PRSPEC ulKind;
    public PROPSPEC_UNION union;

    public PROPSPEC(PRSPEC kind)
    {
        ulKind = kind;
        union = new PROPSPEC_UNION();
    }

    public PROPSPEC(PROPID _propid) : this(PRSPEC.PROPID)
    {
        union.propid = _propid;
    }

    public PROPSPEC(string str) : this(PRSPEC.LPWSTR)
    {
        union.lpwstr = Marshal.StringToCoTaskMemUni(str);
    }
}
 

[StructLayout(LayoutKind.Sequential)]
public struct FULLPROPSPEC
{
	public Guid guidPropSet;
	public PROPSPEC psProperty;
}
 

[StructLayout(LayoutKind.Sequential)]
public struct STAT_CHUNK
{
	public ULONG  idChunk;
    public CHUNK_BREAKTYPE breakType;
    public CHUNKSTATE flags;
	public ULONG locale;
	[MarshalAs(UnmanagedType.Struct)] 
    public FULLPROPSPEC attribute;
	public ULONG idChunkSource;
	public ULONG cwcStartSource;
	public ULONG cwcLenSource;
}


[StructLayout(LayoutKind.Sequential)]
public struct FILTERREGION
{
	public ULONG idChunk;
	public ULONG cwcStart;
	public ULONG cwcExtent;
}

   
[ComImport]
[Guid("89BCB740-6119-101A-BCB7-00DD010655AF")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IFilter
{
	[PreserveSig]
	IFilterReturnCodes Init(
		[MarshalAs(UnmanagedType.U4)] IFILTER_INIT grfFlags, 
		uint cAttributes,
		[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] FULLPROPSPEC[] aAttributes,
		ref uint pdwFlags);

	[PreserveSig] 
	IFilterReturnCodes GetChunk(
        [MarshalAs(UnmanagedType.Struct)] 
        ref STAT_CHUNK pStat);

	[PreserveSig] 
	IFilterReturnCodes GetText(
        ref uint pcwcBuffer, 
        IntPtr awcBuffer);

	[PreserveSig] 
	IFilterReturnCodes GetValue(
        ref IntPtr ppPropValue);

	[PreserveSig] 
	IFilterReturnCodes BindRegion(
		[MarshalAs(UnmanagedType.Struct)] 
        FILTERREGION origPos, 
		ref Guid riid, 
		ref UIntPtr ppunk);
}

public enum IFilterReturnCodes : uint
{
    /// <summary>
    /// Success
    /// </summary>
    S_OK                     = 0,
    /// <summary>
    /// The function was denied access to the filter file. 
    /// </summary>
    E_ACCESSDENIED               = 0x80070005,
    /// <summary>
    /// The function encountered an invalid handle, probably due to alow-memory situation. 
    /// </summary>
    E_HANDLE                  = 0x80070006,
    /// <summary>
    /// The function received an invalid parameter.
    /// </summary>
    E_INVALIDARG               = 0x80070057,
    /// <summary>
    /// Out of memory
    /// </summary>
    E_OUTOFMEMORY               = 0x8007000E,
    /// <summary>
    /// Not implemented
    /// </summary>
    E_NOTIMPL                  = 0x80004001,
    /// <summary>
    /// Unknown error
    /// </summary>
    E_FAIL                     = 0x80000008,
    /// <summary>
    /// File not filtered due to password protection
    /// </summary>
    FILTER_E_PASSWORD            = 0x8004170B,
    /// <summary>
    /// The document format is not recognized by the filter
    /// </summary>
    FILTER_E_UNKNOWNFORMAT         = 0x8004170C,
    /// <summary>
    /// No text in current chunk
    /// </summary>
    FILTER_E_NO_TEXT            = 0x80041705,
    /// <summary>
    /// No values in current chunk
    /// </summary>
	FILTER_E_NO_VALUES         = 0x80041706,
	/// <summary>
    /// No more chunks of text available in object
    /// </summary>
    FILTER_E_END_OF_CHUNKS           = 0x80041700,
    /// <summary>
    /// No more text available in chunk
    /// </summary>
    FILTER_E_NO_MORE_TEXT            = 0x80041701,
    /// <summary>
    /// No more property values available in chunk
    /// </summary>
    FILTER_E_NO_MORE_VALUES          = 0x80041702,
    /// <summary>
    /// Unable to access object
    /// </summary>
    FILTER_E_ACCESS                  = 0x80041703,
    /// <summary>
    /// Moniker doesn't cover entire region
    /// </summary>
    FILTER_W_MONIKER_CLIPPED         = 0x00041704,
    /// <summary>
    /// Unable to bind IFilter for embedded object
    /// </summary>
    FILTER_E_EMBEDDING_UNAVAILABLE   = 0x80041707,
    /// <summary>
    /// Unable to bind IFilter for linked object
    /// </summary>
    FILTER_E_LINK_UNAVAILABLE        = 0x80041708,
    /// <summary>
    ///  This is the last text in the current chunk
    /// </summary>
    FILTER_S_LAST_TEXT               = 0x00041709,
    /// <summary>
    /// This is the last value in the current chunk
    /// </summary>
    FILTER_S_LAST_VALUES             = 0x0004170A
}
